using System;
using System.Collections.Generic;
using System.Text;

using OOFluidFramework.Utility;
using OOFluidFramework;
using OOFluidFramework.Orders;

namespace OOFluidFramework.Operators
{
    class LagrangianOperatorImpl : FluidOperatorImpl, LagrangianOperator
    {
       //Lagrangian Operator is an operator which can move according to its own speed
       //Considering we're retrieving speed from 2 different direction, we indeed need 
       //two independent weight counters.
        protected double XWeightCount;
        protected double YWeightCount;
        protected bool XInterpolated;
        protected bool YInterpolated;

        public LagrangianOperatorImpl(Vector2 InitialLocation, double timeStep, FluidSpaceOperatorRefere spaceRefere)
                :   base(InitialLocation,spaceRefere)
        { XWeightCount = 0; YWeightCount = 0;
        XInterpolated = 0; YInterpolated = 0; }
        
        public Vector2 Move()
        {
            prelo = lo;
            lo = lo + (timeStep * val);
            return lo;
        }
        public int RetrieveVelocity(GridArrangeOrder ord, Delimiter range)
        {
            DistanceLinearInterpolation intpo = new DistanceLinearInterpolation();
            List <EulerianOperator> eulerList = ord.Sieve(range,(LagrangianOperator ) this);
            foreach (EulerianOperator op in eulerList)
            {
                double DistanceP2 = op.ReportDistanceP2(This);
                double Weight = intpo.ReportWeight(DistanceP2);
                ReceiveDirectedSpeed(op.Speed, Weight, op.SpeculateMode);
            }
            return eulerList.Count;
        }

        public int EmitVelocity(GridArrangeOrder ord, Delimiter range)
        {
            Poly6Interpolation intpo = new Poly6Interpolation(2.0);
            List <EulerianOperator> eulerList = ord.Sieve(range, (LagrangianOperator ) this);
            foreach (EulerianOperator op in eulerList)
            {
                op.Receive(val, this, intpo);
            }
            return eulerList.Count;
        }        
        protected double ReceiveDirectedSpeed(double Speed, double Weight, bool Direction)
        {
            switch (Direction)
            {
                case true : //we're facing a X-direction speed
                    val.x += Speed * Weight;
                    XWeightCount += Weight;
                    XInterpolated = true;
                    break;
                case false://the Y-direction speed
                    val.y += Speed * Weight;
                    YWeightCount += Weight;
                    YInterpolated = true;
                    break;
            }
            return Weight;   
        }

        public override Vector2 NormalizeSpeed()
        {
            if (XInterpolated) val.x = val.x / XWeightCount;
            if (YInterpolated) val.y = val.y / YWeightCount;
            XWeightCount = 0;
            XInterpolated = false;
            YWeightCount = 0;
            YInterpolated = false;
            return new Vector2(val);
        }
    }

    interface LagrangianOperator : FluidOperator
    {
        void Move();
        int RetriveVelocity(GridArrangeOrder ord, Delimiter range);
        int EmitVelocity(GridArrangeOrder ord, Delimiter range);
        //***protected*** double ReceiveWeightedSpeed(double WeightedSpeed, bool Direction);
        Vector2 Speed
        {
            get;
            set;
        }
        Vector2 preSpeed
        {
            get;
            set;
        }
    }
}
